package synology.ui;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Hashtable;

import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import synology.SynoApp;
import synology.SynoBean;
import synology.util.Dom;
import synology.util.SynoConstants;

public class ServerTree extends JTree
{
	private DefaultMutableTreeNode _rootNode = null;
	private DefaultTreeModel _treeModel = null;

	private Hashtable<String, SynoNode> _nodeTable = null;


	// ------------------------------------------------------------------------------------
	public ServerTree()
	{
		_nodeTable = new Hashtable<String, SynoNode>();
		initUI();
		try
		{
			initServers();
		}
		catch (IOException e)
		{
		}
	}


	// ------------------------------------------------------------------------------------
	private void initServers()
	throws IOException
	{
		ArrayList<SynoBean> servers = load();
		for (int i = 0; i < servers.size(); ++i)
		{
			addServer(servers.get(i));
		}
	}


	// ------------------------------------------------------------------------------------
	private void initUI()
	{
		addMouseListener(new MouseAdapter()
		{
			public void mousePressed(MouseEvent e)
			{
				if (e.getClickCount() == 2)
				{
					TreePath treePath = getClosestPathForLocation(e.getPoint().x, e.getPoint().y);
					Object[] path = treePath.getPath();
					DefaultMutableTreeNode node = (DefaultMutableTreeNode) path[path.length - 1];

					if (isServerNode(node))
					{
						firePropertyChange("modifyServer", null, ((SynoNode) node).getBean());
					}
				}
			}
		});

		_rootNode = new DefaultMutableTreeNode("Servers");
		_treeModel = new DefaultTreeModel(_rootNode);

		setModel(_treeModel);
		getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
		setShowsRootHandles(true);
	}


	// ------------------------------------------------------------------------------------
	public void addServer(SynoBean bean)
	{
		int index = _rootNode.getChildCount();

		SynoNode node = _nodeTable.get(bean.getName());
		if (node != null)
		{
			index = removeServer(node);
		}

		node = new SynoNode(bean);
		addServer(node, index);

		scrollPathToVisible(new TreePath(node.getPath()));
	}


	// ------------------------------------------------------------------------------------
	private boolean isServerNode(DefaultMutableTreeNode node)
	{
		return (node != _rootNode && node instanceof SynoNode);
	}


	// ------------------------------------------------------------------------------------
	public int removeServer()
	{
		int index = -1;
		Object[] path = getSelectionPath().getPath();
		DefaultMutableTreeNode node = (DefaultMutableTreeNode) path[path.length - 1];
		if (isServerNode(node))
			index = removeServer((SynoNode) node);
		return index;
	}


	// ------------------------------------------------------------------------------------
	private void addServer(SynoNode node, int index)
	{
		_nodeTable.put(node.getBean().getName(), node);
		_treeModel.insertNodeInto(node, _rootNode, index);
		try
		{
			generateBackup();
		}
		catch (Exception e)
		{
		}
	}


	// ------------------------------------------------------------------------------------
	private int removeServer(SynoNode node)
	{
		int index = _treeModel.getIndexOfChild(_rootNode, node);
		_treeModel.removeNodeFromParent(node);
		_nodeTable.remove(node.getBean().getName());
		try
		{
			generateBackup();
		}
		catch (Exception e)
		{
		}

		return index;
	}


	// ------------------------------------------------------------------------------------
	public void modifyServer(String oldName, SynoBean bean)
	{
		SynoNode node = _nodeTable.get(oldName);
		addServer(new SynoNode(bean), removeServer(node));
	}


	// ------------------------------------------------------------------------------------
	private ArrayList<SynoBean> getServerList()
	{
		ArrayList<SynoBean> list = new ArrayList<SynoBean>();

		int max = _treeModel.getChildCount(_rootNode);

		for (int i = 0; i < max; ++i)
		{
			DefaultMutableTreeNode node = (DefaultMutableTreeNode) _treeModel.getChild(_rootNode, i);
			if (node instanceof SynoNode)
			{
				list.add(((SynoNode) node).getBean());
			}
		}

		return list;
	}


	// ------------------------------------------------------------------------------------
	private void generateBackup()
	throws Exception
	{
		save(getServerList());
	}


	public static void main(String[] args)
	{
		SynoBean sb = new SynoBean();
		sb.setName("");
		sb.setProtocol("https");
		sb.setUrl("");
		sb.setPort("5001");
		sb.setLogin("");
		sb.setPassword("");

		SynoBean sb1 = new SynoBean();
		sb1.setName("");
		sb1.setProtocol("https");
		sb1.setUrl("");
		sb1.setPort("5001");
		sb1.setLogin("");
		sb1.setPassword("");

		ArrayList<SynoBean> list = new ArrayList<SynoBean>();
		list.add(sb);
		list.add(sb1);

		ServerTree tree = new ServerTree();
		try
		{
			tree.save(list);
		}
		catch (Exception e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		try
		{
			list = tree.load();
		}
		catch (IOException e)
		{
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		for (int i = 0; i < list.size(); ++i)
		{
			System.out.println("ServerTree.main() - " + list.get(i) + " (list.get(i))");
		}
	}


	// ------------------------------------------------------------------------------------
	synchronized private void save(ArrayList<SynoBean> list)
	throws Exception
	{
		String appDir = SynoApp.getApplicationDirectory();
		File dir = new File(appDir);
		if (!dir.exists())
			dir.mkdirs();

		String appFile = appDir + SynoApp.getSeparator() + SynoConstants.XML_NAME;
		File file = new File(appFile);
		Document doc = Dom.newDocument();

		Element rootNode = doc.createElement(SynoConstants.SERVERS_NODE);
		doc.appendChild(rootNode);

		for (int i = 0; i < list.size(); ++i)
		{
			Element elem;
			try
			{
				elem = list.get(i).buildElement();
				elem = (Element) doc.importNode(elem, true);
				rootNode.appendChild(elem);
			}
			catch (ParserConfigurationException e)
			{
			}
		}

		Dom.saveXML(doc, new BufferedOutputStream(new FileOutputStream(file)));
	}


	synchronized private ArrayList<SynoBean> load()
	throws IOException
	{
		String appDir = SynoApp.getApplicationDirectory();
		File dir = new File(appDir);
		if (!dir.exists())
			dir.mkdirs();

		String appFile = appDir + SynoApp.getSeparator() + SynoConstants.XML_NAME;
		File file = new File(appFile);
		Document doc = null;
		if (!file.exists())
		{
			file.createNewFile();
			try
			{
				doc = Dom.newDocument();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}
		}
		else
		{
			try
			{
				doc = Dom.loadXML(new BufferedInputStream(new FileInputStream(file)));
			}
			catch (Exception e)
			{
				try
				{
					doc = Dom.newDocument();
				}
				catch (Exception e1)
				{
				}
			}
		}

		ArrayList<SynoBean> loadList = new ArrayList<SynoBean>();
		if (doc != null)
		{
			NodeList list = doc.getElementsByTagName(SynoConstants.SERVER_NODE);
			for (int i = 0; i < list.getLength(); ++i)
			{
				Element elem = (Element) list.item(i);
				SynoBean bean = SynoBean.createSynoBean(elem);
				if (bean != null)
					loadList.add(bean);
			}
		}

		return loadList;
	}
}

class SynoNode extends DefaultMutableTreeNode
{
	private SynoBean _bean;


	// ------------------------------------------------------------------------------------
	SynoNode(SynoBean bean)
	{
		super(bean.getName());
		this._bean = bean;
	}


	// ------------------------------------------------------------------------------------
	SynoBean getBean()
	{
		return _bean;
	}
}
